package com.yc.sole.erp.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.yc.sole.common.constant.PubConstant;
import com.yc.sole.common.domain.StateCountResp;
import com.yc.sole.erp.baiduOcr.OcrResult;
import com.yc.sole.erp.baiduOcr.OcrService;
import com.yc.sole.erp.baiduOcr.ProduceResult;
import com.yc.sole.erp.baiduOcr.TableColumn;
import com.yc.sole.erp.dto.OutputDto;
import com.yc.sole.erp.dto.ProduceDto;
import com.yc.sole.erp.dto.ProductDto;
import com.yc.sole.erp.dto.ShiftDto;
import com.yc.sole.erp.entity.Product;
import com.yc.sole.erp.exception.ErpResultEnum;
import com.yc.sole.erp.repository.OutputRepository;
import com.yc.sole.erp.repository.ProduceRepository;
import com.yc.sole.erp.repository.ProductRepository;
import com.yc.sole.erp.repository.ShiftRepository;
import com.yc.sole.erp.request.OutputBatchReq;
import com.yc.sole.erp.request.OutputQueryReq;
import com.yc.sole.erp.request.OutputReq;
import com.yc.sole.erp.response.OutputResp;
import com.yc.sole.erp.response.ProductResp;
import com.yc.sole.framework.exception.BaseException;
import com.yc.sole.framework.mybatis.page.Paging;
import com.yc.sole.framework.mybatis.util.PageUtil;
import com.yc.sole.framework.redis.RedissonService;
import com.yc.sole.framework.redis.enums.CacheTime;
import com.yc.sole.system.service.SequenceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务接口实现
 *
 * @author yizuomin
 * @date 2024-01-15
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class OutputBiz {

    private final OutputRepository outputRepository;
    private final ShiftRepository shiftRepository;
    private final ProduceRepository produceRepository;
    private final SequenceService sequenceService;
    private final ProductRepository productRepository;
    private final OcrService ocrService;
    private final RedissonService redissonService;

    /**
     * 产量登记
     *
     * @param outputReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOutput(OutputReq outputReq) {
        // 验证是否重复
        Integer count = outputRepository.countByShiftIdAndOutputDateProduceId(outputReq.getShiftId(), outputReq.getOutputDate(), outputReq.getProduceId(), null);
        if (count > 0) {
            throw new BaseException(ErpResultEnum.OUTPUT_REG_REPEAT, count);
        }
        // 查询生产
        ProduceDto produceDto = produceRepository.selectProduceById(outputReq.getProduceId());
        if (Objects.isNull(produceDto)) {
            throw new BaseException(ErpResultEnum.PRODUCE_NOT_FOUND);
        }
        if (produceDto.getRemainingQuantity() < outputReq.getQuantity()) {
            throw new BaseException(ErpResultEnum.OUTPUT_QUANTITY_ERROR, outputReq.getQuantity(), produceDto.getRemainingQuantity());
        }
        // 查询班次
        ShiftDto shiftDto = shiftRepository.selectShiftById(outputReq.getShiftId());
        if (Objects.isNull(shiftDto)) {
            throw new BaseException(ErpResultEnum.SHIFT_NOT_EXIST);
        }
        // 查询产品
        ProductDto productDto = productRepository.selectProductById(produceDto.getProductId());
        if (Objects.isNull(productDto)) {
            throw new BaseException(ErpResultEnum.PRODUCT_NOT_FOUND);
        }
        // 生成产量编号
        String outputNo = sequenceService.generateSeqNo(PubConstant.OUTPUT_SEQ_KEY);
        OutputDto outputDto = new OutputDto();
        outputDto.setOutputNo(outputNo);
        outputDto.setShiftId(shiftDto.getId());
        outputDto.setShiftName(shiftDto.getName());
        outputDto.setProduceId(produceDto.getId());
        outputDto.setProduceNo(produceDto.getProduceNo());
        outputDto.setProductId(productDto.getId());
        outputDto.setProductName(productDto.getProductName());
        outputDto.setQuantity(outputReq.getQuantity());
        outputDto.setOutputDate(outputReq.getOutputDate());
        outputRepository.saveOutput(outputDto);
        // 减少生产余数
        produceRepository.updateRemainingQuantity(produceDto.getId(), -outputDto.getQuantity());
        // 产品增加库存
        productRepository.updateStock(productDto.getId(), outputReq.getQuantity(), outputReq.getOutputDate() + shiftDto.getName() + "生产");
    }

    /**
     * 修改班组产量
     *
     * @param outputReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateOutputById(OutputReq outputReq) {
        OutputDto outputDto = outputRepository.selectOutputById(outputReq.getId());
        if (Objects.isNull(outputDto)) {
            throw new BaseException(ErpResultEnum.OUTPUT_NOT_FOUND);
        }
        // 验证是否重复
        Integer count = outputRepository.countByShiftIdAndOutputDateProduceId(outputReq.getShiftId(), outputReq.getOutputDate(), outputReq.getProduceId(), outputDto.getId());
        if (count > 1) {
            throw new BaseException(ErpResultEnum.OUTPUT_REG_REPEAT, count);
        }
        // 判断剩余数量
        ProduceDto produceDto = produceRepository.selectProduceById(outputDto.getProduceId());
        if (Objects.isNull(produceDto)) {
            throw new BaseException(ErpResultEnum.PRODUCE_NOT_FOUND);
        }
        // 查询班次
        ShiftDto shiftDto = shiftRepository.selectShiftById(outputReq.getShiftId());
        if (Objects.isNull(shiftDto)) {
            throw new BaseException(ErpResultEnum.SHIFT_NOT_EXIST);
        }
        // 排产id不能修改
        if (!outputDto.getProduceId().equals(outputReq.getProduceId())) {
            throw new BaseException("排产信息不能修改");
        }
        OutputDto updateDto = new OutputDto();
        updateDto.setId(outputDto.getId());
        updateDto.setShiftId(shiftDto.getId());
        updateDto.setShiftName(shiftDto.getName());
        updateDto.setProduceId(produceDto.getId());
        updateDto.setProduceNo(produceDto.getProduceNo());
        updateDto.setProductId(produceDto.getProductId());
        updateDto.setProductName(produceDto.getProductName());
        updateDto.setQuantity(outputReq.getQuantity());
        updateDto.setOutputDate(outputReq.getOutputDate());
        outputRepository.updateOutputById(updateDto);
        if (!outputDto.getQuantity().equals(outputReq.getQuantity())) {
            // 增加或减少生产余数
            produceRepository.updateRemainingQuantity(produceDto.getId(), outputDto.getQuantity() - outputReq.getQuantity());
            // 增加或减少库存
            productRepository.updateStock(produceDto.getProductId(), outputReq.getQuantity() - outputDto.getQuantity(), outputReq.getOutputDate() + shiftDto.getName() + "生产");
        }
    }

    /**
     * 删除
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteOutput(Collection<Long> ids) {
        for (Long id : ids) {
            // 查询产量记录
            OutputDto outputDto = outputRepository.selectOutputById(id);
            if (Objects.isNull(outputDto)) {
                throw new BaseException(ErpResultEnum.OUTPUT_NOT_FOUND);
            }
            // 增加生产余数
            produceRepository.updateRemainingQuantity(outputDto.getProduceId(), outputDto.getQuantity());
            // 减少库存数量
            productRepository.updateStock(outputDto.getProductId(), -outputDto.getQuantity(), outputDto.getOutputDate() + outputDto.getShiftName() + "删除产量");
        }
        // 删除产量
        outputRepository.deleteOutputByIds(ids);
    }

    /**
     * 分页查询
     *
     * @param page
     * @param queryReq
     * @return
     */
    public Paging<OutputResp> selectOutputPage(Paging<OutputQueryReq> page, OutputQueryReq queryReq) {
        Paging<OutputResp> outputRespPaging = PageUtil.pageCopy(outputRepository.selectOutputPage(page, queryReq), OutputResp.class);
        if (CollUtil.isEmpty(outputRespPaging.getRecords())) {
            return outputRespPaging;
        }
        Set<Long> productIds = outputRespPaging.getRecords().stream().map(OutputResp::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDto> productDtoMap = productRepository.selectProductByIds(productIds).stream().collect(Collectors.toMap(ProductDto::getId, v -> v));
        for (OutputResp outputResp : outputRespPaging.getRecords()) {
            ProductDto productDto = productDtoMap.get(outputResp.getProductId());
            if (Objects.nonNull(productDto)) {
                outputResp.setProductInfo(BeanUtil.copyProperties(productDto, ProductResp.class));
            }
        }
        return outputRespPaging;
    }

    /**
     * 通过id查询班组产量
     *
     * @param id
     * @return
     */
    public OutputResp selectOutputById(Long id) {
        OutputResp outputResp = BeanUtil.copyProperties(outputRepository.selectOutputById(id), OutputResp.class);
        if (Objects.isNull(outputResp)) {
            return null;
        }
        ProduceDto produceDto = produceRepository.selectProduceById(outputResp.getProduceId());
        if (Objects.isNull(produceDto)) {
            throw new BaseException(ErpResultEnum.PRODUCE_NOT_FOUND);
        }
        ProductDto productDto = productRepository.selectProductById(outputResp.getProductId());
        if (Objects.nonNull(productDto)) {
            outputResp.setProductInfo(BeanUtil.copyProperties(productDto, ProductResp.class));
        }
        outputResp.setCustomerName(produceDto.getCustomerName());
        outputResp.setTotalQuantity(produceDto.getQuantity());
        outputResp.setRemainingQuantity(produceDto.getRemainingQuantity());
        return outputResp;
    }

    /**
     * 根据日期和班次id查询产量
     *
     * @param outputDate
     * @param shiftId
     * @return
     */
    public Integer selectByShiftIdAndOutputDate(Long shiftId, String outputDate) {
        List<OutputDto> outputDtoList = outputRepository.selectByShiftIdAndOutputDate(shiftId, outputDate);
        return outputDtoList.stream().map(OutputDto::getQuantity).reduce(0, Integer::sum);
    }

    /**
     * 查询班次数量
     *
     * @return
     */
    public List<StateCountResp> selectShiftCount(OutputQueryReq queryReq) {
        List<StateCountResp> countRespList = outputRepository.selectShiftCount(queryReq);
        List<ShiftDto> shiftDtoList = shiftRepository.selectAllShift();
        Long totalCount = countRespList.stream().map(StateCountResp::getCount).reduce(0L, Long::sum);
        ArrayList<StateCountResp> resultList = Lists.newArrayList(new StateCountResp("-1", "全部", totalCount));
        Map<Long, StateCountResp> stateCountRespMap = countRespList.stream().collect(Collectors.toMap(e -> Long.valueOf(e.getState()), v -> v));
        List<StateCountResp> respList = shiftDtoList.stream().map(shiftDto -> {
            StateCountResp countResp = stateCountRespMap.get(shiftDto.getId());
            if (Objects.isNull(countResp)) {
                countResp = new StateCountResp();
                countResp.setCount(0L);
            }
            countResp.setStateName(shiftDto.getName());
            countResp.setState(String.valueOf(shiftDto.getId()));
            return countResp;
        }).toList();
        resultList.addAll(respList);
        return resultList;
    }

    /**
     * 查询当前查询条件的总产量
     *
     * @return
     */
    public List<StateCountResp> selectOutputQuantity(OutputQueryReq queryReq) {
        return outputRepository.selectOutputQuantity(queryReq);
    }

    /**
     * ocr识别
     *
     * @param file
     * @return
     */
    public List<OutputResp> ocr(MultipartFile file) {
        Long count = redissonService.incrementLongAndExpire(DateUtil.today() + ":ocr_count", CacheTime.SEVEN_DAY);
        if (count > 16) {
            throw new BaseException("每天最多允许16次，今日操作过于频繁，已暂停使用，请明天再试");
        }
        String result = ocrService.doOcr(file);
        OcrResult ocrResult = JSONUtil.toBean(result, OcrResult.class);
        List<ProduceResult> resultList = ocrResult.getTables_result().stream().map(tableResult -> {
            List<TableColumn> tableColumnList = tableResult.getBody();
            // 去表头
            tableColumnList = tableColumnList.stream().filter(e -> e.getRow_start() > 0).toList();
            // 每一行内容分组
            Map<String, List<TableColumn>> columnMap = tableColumnList.stream().collect(Collectors.groupingBy(e -> e.getRow_start() + "-" + e.getRow_end()));
            return columnMap.keySet().stream().map(key -> {
                List<TableColumn> tableColumns = columnMap.get(key);
                ProduceResult produceResult = new ProduceResult();
                for (TableColumn tableColumn : tableColumns) {
                    produceResult.setIndex(tableColumn.getRow_start());
                    if (tableColumn.getCol_start() == 0 && tableColumn.getCol_end() == 1) {
                        produceResult.setProduceNo(tableColumn.getWords());
                    }
                    if (tableColumn.getCol_start() == 6 && tableColumn.getCol_end() == 7) {
                        produceResult.setQuantity(tableColumn.getWords());
                    }
                }
                return produceResult;
            }).toList();
        }).flatMap(Collection::stream).toList();
        resultList = resultList.stream().sorted(Comparator.comparing(ProduceResult::getIndex)).toList();

        Set<String> produceNoList = resultList.stream().map(ProduceResult::getProduceNo).collect(Collectors.toSet());
        Map<String, ProduceDto> produceDtoMap = produceRepository.selectByProduceNos(produceNoList).stream().collect(Collectors.toMap(ProduceDto::getProduceNo, v -> v));
        return resultList.stream().map(r -> {
            ProduceDto produceDto = produceDtoMap.get(r.getProduceNo());
            OutputResp outputResp = new OutputResp();
            outputResp.setProduceNo(r.getProduceNo());
            if (Objects.nonNull(produceDto)) {
                outputResp.setProductName(produceDto.getProductName());
            }
            outputResp.setQuantity(Integer.valueOf(r.getQuantity()));
            return outputResp;
        }).toList();
    }

    /**
     * 批量保存排产信息
     *
     * @param batchReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOutputBatch(OutputBatchReq batchReq) {
        // 查询班次
        ShiftDto shiftDto = shiftRepository.selectShiftById(batchReq.getShiftId());
        if (Objects.isNull(shiftDto)) {
            throw new BaseException(ErpResultEnum.SHIFT_NOT_EXIST);
        }
        // 查询排产
        Set<String> produceNos = batchReq.getDataList().stream().map(OutputBatchReq.OutputBatchDetailReq::getProduceNo).collect(Collectors.toSet());
        List<ProduceDto> produceDtoList = produceRepository.selectByProduceNos(produceNos);
        Map<String, ProduceDto> produceDtoMap = produceDtoList.stream().collect(Collectors.toMap(ProduceDto::getProduceNo, v -> v));
        // 查询产品
        Set<Long> productIds = produceDtoList.stream().map(ProduceDto::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDto> productDtoMap = productRepository.selectProductByIds(productIds).stream().collect(Collectors.toMap(Product::getId, v -> v));
        for (OutputBatchReq.OutputBatchDetailReq detailReq : batchReq.getDataList()) {
            ProduceDto produceDto = produceDtoMap.get(detailReq.getProduceNo());
            ProductDto productDto = productDtoMap.get(produceDto.getProductId());

            // 查询之前是否已登记产量
            Integer integer = outputRepository.countByShiftIdAndOutputDateProduceId(shiftDto.getId(), batchReq.getOutputDate(), produceDto.getId(), null);
            if (integer > 0) {
                // 已登记
                OutputDto outputDto = outputRepository.selectByShiftIdAndOutputDateProduceId(shiftDto.getId(), batchReq.getOutputDate(), produceDto.getId(), null);
                OutputDto updateDto = new OutputDto();
                updateDto.setId(outputDto.getId());
                updateDto.setShiftId(shiftDto.getId());
                updateDto.setShiftName(shiftDto.getName());
                updateDto.setProduceId(produceDto.getId());
                updateDto.setProduceNo(produceDto.getProduceNo());
                updateDto.setProductId(produceDto.getProductId());
                updateDto.setProductName(produceDto.getProductName());
                updateDto.setQuantity(detailReq.getQuantity());
                updateDto.setOutputDate(batchReq.getOutputDate());
                outputRepository.updateOutputById(updateDto);

                if (!outputDto.getQuantity().equals(detailReq.getQuantity())) {
                    // 增加或减少生产余数
                    produceRepository.updateRemainingQuantity(produceDto.getId(), outputDto.getQuantity() - detailReq.getQuantity());
                    // 变动库存
                    productRepository.updateStock(produceDto.getProductId(), detailReq.getQuantity() - outputDto.getQuantity(), batchReq.getOutputDate() + shiftDto.getName() + "修改产量");
                }
            } else {
                // 未登记

                // 生成产量编号
                String outputNo = sequenceService.generateSeqNo(PubConstant.OUTPUT_SEQ_KEY);
                OutputDto outputDto = new OutputDto();
                outputDto.setOutputNo(outputNo);
                outputDto.setShiftId(shiftDto.getId());
                outputDto.setShiftName(shiftDto.getName());
                outputDto.setProduceId(produceDto.getId());
                outputDto.setProduceNo(produceDto.getProduceNo());
                outputDto.setProductId(productDto.getId());
                outputDto.setProductName(productDto.getProductName());
                outputDto.setQuantity(detailReq.getQuantity());
                outputDto.setOutputDate(batchReq.getOutputDate());
                outputRepository.saveOutput(outputDto);
                // 减少生产余数
                produceRepository.updateRemainingQuantity(produceDto.getId(), -outputDto.getQuantity());
                // 产品增加库存
                productRepository.updateStock(productDto.getId(), detailReq.getQuantity(), batchReq.getOutputDate() + shiftDto.getName() + "生产");
            }
        }
    }
}
